package com.cqfae.deploycentral.app.services.impl;

import com.alibaba.fastjson.JSONObject;
import com.cqfae.deploycentral.app.base.BaseConstantCode;
import com.cqfae.deploycentral.app.base.BaseConstants;
import com.cqfae.deploycentral.app.config.DeployInfo;
import com.cqfae.deploycentral.app.config.JenkinsInfo;
import com.cqfae.deploycentral.app.config.WebSocketInfo;
import com.cqfae.deploycentral.app.dao.entity.AppInfo;
import com.cqfae.deploycentral.app.dao.entity.ReportInfoTag;
import com.cqfae.deploycentral.app.dao.entity.ReportJira;
import com.cqfae.deploycentral.app.dao.entity.WindowPlansTag;
import com.cqfae.deploycentral.app.dao.mapper.*;
import com.cqfae.deploycentral.app.services.JenkinsService;
import com.cqfae.deploycentral.app.services.JiraService;
import com.cqfae.deploycentral.app.services.RemoteService;
import com.cqfae.deploycentral.app.services.WindowPlansService;
import com.cqfae.deploycentral.app.utils.GsonUtils;
import com.cqfae.deploycentral.app.web.enums.DeployPercentEnum;
import com.cqfae.deploycentral.app.web.enums.ProgressColorEnum;
import com.cqfae.deploycentral.app.web.vo.DeployHistoryVo;
import com.cqfae.deploycentral.app.web.vo.JenkinsLogVo;
import com.cqfae.deploycentral.app.web.vo.WindowPlansTagVo;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by YANGHENG940 on 2017/7/12.
 */
@Service
public class JenkinsServiceImpl implements JenkinsService {

    private static Logger logger = LoggerFactory.getLogger(WindowPlansServiceImpl.class);

    @Autowired
    private RemoteService remoteService;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private WindowPlansTagMapper windowPlansTagMapper;


    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @Autowired
    private WebSocketInfo webSocketInfo;

    @Autowired
    private ReportInfoTagMapper reportInfoTagMapper;

    @Autowired
    private DeployInfo deployInfo;

    @Autowired
    private JenkinsInfo jenkinsInfo;

    @Autowired
    private WindowPlansService windowPlansService;

    @Autowired
    private AppInfoMapper appInfoMapper;

    @Autowired
    private ReportTestLinkMapper reportTestLinkMapper;

    @Autowired
    private JiraService jiraService;

    @Autowired
    private ReportJiraMapper reportJiraMapper;

    private String sqlReportCode = "reportCode";

    /**
     * 守护线程，当超过一定时限Jenkins没有回调时，保证项目继续执行
     *
     * @param reportCode
     */
    public void watchJenkins(String reportCode) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                long executeTimeLimit = Long.parseLong(deployInfo.getExecuteTimeLimit()) * 1000;
                while (true) {
                    // 等待ExecuteTimeLimit秒
                    sleepSeconds(executeTimeLimit);

                    // 保存所有所有执行中
                    List<Integer> executeFlags = new ArrayList<>();
                    executeFlags.add(BaseConstantCode.DEPLOY_STATUS_PROCESSING);
                    executeFlags.add(BaseConstantCode.JENKINS_STATUS_GIT_OK);
                    executeFlags.add(BaseConstantCode.JENKINS_STATUS_BUILD_OK);
                    executeFlags.add(BaseConstantCode.JENKINS_STATUS_INPUT_OK);
                    executeFlags.add(BaseConstantCode.JENKINS_STATUS_OUTPUT_OK);
                    executeFlags.add(BaseConstantCode.JENKINS_STATUS_DEPLOY_OK);

                    // 查询执行中的WindowPlansTag
                    Example example = new Example(WindowPlansTag.class);
                    Example.Criteria criteria = example.createCriteria();
                    criteria.andIn("deployStatus", executeFlags);
                    criteria.andEqualTo(sqlReportCode, reportCode);
                    List<WindowPlansTag> windowPlansTagList = windowPlansTagMapper.selectByExample(example);

                    // 判断是否有处于发布中windowPlansTag
                    if (windowPlansTagList != null && !windowPlansTagList.isEmpty()) {
                        WindowPlansTag windowPlansTag = windowPlansTagList.get(0);
                        // 判断处于发布中的windowPlansTag时间是否超过ExecuteTimeLimit
                        long deployProcessingTime = new Date().getTime() - windowPlansTag.getUpdateTime().getTime();
                        if (deployProcessingTime > executeTimeLimit) {
                            // 主动查询windowPlansTag状态
                            ReportInfoTag reportInfoTag = reportInfoTagMapper.selectByPrimaryKey(windowPlansTag.getReportTagId());
                            if (isWindowPlansTagBuilding(reportInfoTag.getAppName())) {
                                // 当windowPlansTag执行超过限制时间并还在执行则每10秒去查询其状态
                                while (true) {
                                    sleepSeconds(10000);
                                    if (!isWindowPlansTagBuilding(reportInfoTag.getAppName())) {
                                        break;
                                    }
                                }
                            }
                            // 更新状态
                            updateBuildResult(reportInfoTag);
                            // 重置线程沉睡时间
                            executeTimeLimit = Long.parseLong(deployInfo.getExecuteTimeLimit()) * 1000;
                            // 避免Jenkins执行超时回调的情况下快速调用Jenkins两次，避免出现并行
                            if (!isWindowPlansBuilding(windowPlansTag.getReportCode())) {
                                // 调用下一个invokeJenkins
                                invokeJenkins(reportCode);
                            }

                        } else {
                            executeTimeLimit = Long.parseLong(deployInfo.getExecuteTimeLimit()) * 1000 > deployProcessingTime ?
                                    Long.parseLong(deployInfo.getExecuteTimeLimit()) * 1000 - deployProcessingTime : Long.parseLong(deployInfo.getExecuteTimeLimit()) * 1000;
                        }
                    } else {
                        break;
                    }
                }
            }
        };

        // 如有WindowPlansTag处于正在运行时不再开启守护线程
        if (!isWindowPlansBuilding(reportCode)) {
            thread.start();
        }

    }

    /**
     * 调用Jenkins
     *
     * @param reportCode
     */
    public void invokeJenkins(String reportCode) {

        // 沉睡时间，避免socket发送过快
        sleepSeconds(1000);

        // 首先判断是否有项目正在执行，保证单个reportCode为串行执行
        if (isWindowPlansBuilding(reportCode)) {
            return;
        }

        // 查找处于待发布的所有windowPlansTag
        Example exampleWindowPlansTag = new Example(WindowPlansTag.class);
        Example.Criteria criteria = exampleWindowPlansTag.createCriteria();
        criteria.andEqualTo(sqlReportCode, reportCode);
        criteria.andEqualTo("deployStatus", BaseConstantCode.DEPLOY_STATUS_WAITING);
        List<WindowPlansTag> windowPlansTagList = windowPlansTagMapper.selectByExample(exampleWindowPlansTag);
        // 存储待执行的TagId
        List<Long> reportIdList = new ArrayList<>();
        // 没有处于待发布的程序，所有发布执行完成
        if (windowPlansTagList == null || windowPlansTagList.isEmpty()) {
            // 是否更新jira状态布尔
            boolean isJiraUpadte = false;
            // 查询测试报告发布状态
            exampleWindowPlansTag.clear();
            exampleWindowPlansTag.createCriteria().andEqualTo(sqlReportCode, reportCode);
            List<WindowPlansTag> windowPlansTagListResult = windowPlansTagMapper.selectByExample(exampleWindowPlansTag);
            if (windowPlansTagListResult != null && !windowPlansTagListResult.isEmpty()) {
                for (WindowPlansTag windowPlansTag : windowPlansTagListResult) {
                    if (BaseConstantCode.JENKINS_STATUS_CHECK_OK != windowPlansTag.getDeployStatus().intValue()) {
                        isJiraUpadte = false;
                        break;
                    }
                }
            }

            if (isJiraUpadte) {
                // 更新jira系统状态
                int jiraResult = jiraService.changeVersionStatus(reportCode);
                if (jiraResult == BaseConstantCode.SCHEDULED_EXEC_SUCCESS) {
                    // 更新本地数据库jira
                    Example exampleReportJira = new Example(ReportJira.class);
                    exampleReportJira.createCriteria().andEqualTo(sqlReportCode, reportCode);
                    ReportJira reportJira = new ReportJira();
                    reportJira.setIssuesStatus(BaseConstants.getConstantsCodeMap().get("jiraStatus.done"));
                    reportJira.setUpdateBy("Jenkins");
                    reportJira.setUpdateTime(new Date());
                    reportJiraMapper.updateByExampleSelective(reportJira, exampleReportJira);
                }
            }
            return;
        } else {
            for (WindowPlansTag windowPlansTag : windowPlansTagList) {
                reportIdList.add(windowPlansTag.getReportTagId());
            }
        }

        // 以优先级升序的结果排序
        Example exampleReportTag = new Example(ReportInfoTag.class);
        exampleReportTag.orderBy("level");
        Example.Criteria criteriaReportTag = exampleReportTag.createCriteria();
        criteriaReportTag.andEqualTo(sqlReportCode, reportCode);
        criteriaReportTag.andIn("id", reportIdList);
        List<ReportInfoTag> tags = reportInfoTagMapper.selectByExample(exampleReportTag);
        // 取level级别最高的windowPlansTag
        List<WindowPlansTagVo> vos = windowPlansService.getWindowPlansTags(tags);
        WindowPlansTagVo windowPlansTagVo = vos.get(0);

        // 将windowPlanTag修改为正在发布
        WindowPlansTag windowPlansTag = new WindowPlansTag();
        windowPlansTag.setUpdateBy("123");
        windowPlansTag.setUpdateTime(new Date());
        windowPlansTag.setDeployStatus(BaseConstantCode.DEPLOY_STATUS_PROCESSING);
        Example example = new Example(WindowPlansTag.class);
        example.createCriteria().andEqualTo("reportTagId", windowPlansTagVo.getReportTagId());
        windowPlansTagMapper.updateByExampleSelective(windowPlansTag, example);

        // 获取相关ReportInfoTag信息,打开socket更新发布显示页面
        Long reportTagId = windowPlansTagVo.getReportTagId();
        ReportInfoTag reportInfoTag = reportInfoTagMapper.selectByPrimaryKey(reportTagId);

        // 设置socket返回结果
        messagingToView(reportInfoTag, null);

        remoteService.invokeJenkins(windowPlansTagVo, deployInfo.getAction());
    }

    /**
     * 查询最近一个构建日志
     *
     * @param appName
     */
    public JenkinsLogVo getLog(String appName) {
        // 获取appName在Jenkins中对应的实际执行名称
        AppInfo appInfo = new AppInfo();
        appInfo.setAppName(appName);
        appInfo = appInfoMapper.selectOne(appInfo);
        String executeName = appInfo.getBuildUrl().substring(1);
        executeName = executeName.substring(0, executeName.lastIndexOf('/'));
        StringBuilder sb = new StringBuilder();
        sb.append(jenkinsInfo.getHost());
        sb.append(executeName);
        sb.append(jenkinsInfo.getLastLogUrl());
        String sbStr = sb.toString();
        logger.info("getLog call url:{}", sbStr);
        ResponseEntity<String> responseEntity = restTemplate.exchange(sb.toString(),
                HttpMethod.GET, null, String.class);
        String responseEntityStr = GsonUtils.getInstance().toJson(responseEntity);
        logger.info("getLog return resultCode:{}", responseEntityStr);
        String logMessage = responseEntity.getBody();
        logMessage = logMessage.replace("\r\n", "\r");
        JenkinsLogVo jenkinsLogVo = new JenkinsLogVo();
        jenkinsLogVo.setLogMessage(logMessage.split("\r"));
        return jenkinsLogVo;
    }

    /**
     * 向Jenkins查询单个项目执行状态
     *
     * @param appName
     * @return
     */
    private boolean isWindowPlansTagBuilding(String appName) {
        // 获取appName在Jenkins中对应的实际执行名称
        AppInfo appInfo = new AppInfo();
        appInfo.setAppName(appName);
        appInfo = appInfoMapper.selectOne(appInfo);
        String executeName = appInfo.getBuildUrl().substring(1);
        executeName = executeName.substring(0, executeName.lastIndexOf('/'));
        // 在执行完成的情况下跳出循环
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append(jenkinsInfo.getHost());
        stringBuffer.append(executeName);
        stringBuffer.append(jenkinsInfo.getLastBuildUrl());
        String remoteUrl = stringBuffer.toString();
        logger.info("isWindowPlansTagBuilding call url:{}", remoteUrl);
        ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(remoteUrl,
                HttpMethod.GET, null, JSONObject.class);
        String responseEntityStr = GsonUtils.getInstance().toJson(responseEntity);
        logger.info("isWindowPlansTagBuilding return resultCode:{}", responseEntityStr);
        JSONObject responseJson = responseEntity.getBody();
        return responseJson.getObject("building", boolean.class);
    }

    /**
     * 获得构建结果
     *
     * @param reportInfoTag
     * @return
     */
    private void updateBuildResult(ReportInfoTag reportInfoTag) {
        // 获取appName在Jenkins中对应的实际执行名称
        AppInfo appInfo = new AppInfo();
        appInfo.setAppName(reportInfoTag.getAppName());
        appInfo = appInfoMapper.selectOne(appInfo);
        String executeName = appInfo.getBuildUrl().substring(1);
        executeName = executeName.substring(0, executeName.lastIndexOf('/'));
        // 获取构建结果
        StringBuilder stringBuffer = new StringBuilder();
        stringBuffer.append(jenkinsInfo.getHost());
        stringBuffer.append(executeName);
        stringBuffer.append(jenkinsInfo.getLastBuildUrl());
        String remoteUrl = stringBuffer.toString();
        logger.info("updateBuildResult call url:{}", remoteUrl);
        ResponseEntity<JSONObject> responseEntity = restTemplate.exchange(remoteUrl,
                HttpMethod.GET, null, JSONObject.class);
        String responseEntityStr = GsonUtils.getInstance().toJson(responseEntity);
        logger.info("updateBuildResult result result:{}", responseEntityStr);
        JSONObject responseJson = responseEntity.getBody();
        String buildResult = responseJson.getObject("result", String.class);
        WindowPlansTag windowPlansTag = new WindowPlansTag();
        // 根据结果，成功设置为JENKINS_STATUS_CHECK_OK，失败设为DEPLOY_STATUS_FAIL
        if ("SUCCESS".equals(buildResult)) {
            windowPlansTag.setDeployStatus(BaseConstantCode.JENKINS_STATUS_CHECK_OK);
        } else {
            windowPlansTag.setDeployStatus(BaseConstantCode.DEPLOY_STATUS_FAIL);
        }
        windowPlansTag.setUpdateBy("123");
        windowPlansTag.setUpdateTime(new Date());
        Example example = new Example(WindowPlansTag.class);
        example.createCriteria().andEqualTo("reportTagId", reportInfoTag.getId());
        windowPlansTagMapper.updateByExampleSelective(windowPlansTag, example);

        sleepSeconds(1000);
        // 设置socket返回结果
        messagingToView(reportInfoTag, windowPlansTag.getDeployStatus());

    }

    /**
     * 设置socket返回结果
     *
     * @param reportInfoTag
     */
    private void messagingToView(ReportInfoTag reportInfoTag, Integer deploy) {
        DeployHistoryVo vo = new DeployHistoryVo(deploy != null ? deploy.intValue() : BaseConstantCode.DEPLOY_STATUS_PROCESSING);
        vo.setReportCode(reportInfoTag.getReportCode());
        vo.setAppName(reportInfoTag.getAppName());
        vo.setExecutePercent(DeployPercentEnum.value(deploy != null ? deploy.intValue() : BaseConstantCode.DEPLOY_STATUS_PROCESSING));
        vo.setProgressColor(ProgressColorEnum.value(deploy != null ? deploy.intValue() : BaseConstantCode.DEPLOY_STATUS_PROCESSING));
        vo.setTag(reportInfoTag.getTagInfo());
        messagingTemplate.convertAndSend(webSocketInfo.getSubscribe(), GsonUtils.getInstance().toJson(vo));

    }

    /**
     * 向数据库查询，扫描WindowPlans是否有项目正在执行
     *
     * @return
     */
    private boolean isWindowPlansBuilding(String reportCode) {
        // 参数保护
        if (StringUtils.isBlank(reportCode)) {
            return false;
        }
        // 查询reportCode关联的所有TAG
        WindowPlansTag windowPlansTag = new WindowPlansTag();
        windowPlansTag.setReportCode(reportCode);
        List<WindowPlansTag> windowPlansTagList = windowPlansTagMapper.select(windowPlansTag);
        if (windowPlansTagList != null && !windowPlansTagList.isEmpty()) {
            for (WindowPlansTag windowPlansTag1 : windowPlansTagList) {
                int deployStatus = windowPlansTag1.getDeployStatus().intValue();
                // 判断是否有WindowPlansTag正在执行时
                boolean isExecuting = ArrayUtils.contains(new int[]{
                        BaseConstantCode.DEPLOY_STATUS_PROCESSING,
                        BaseConstantCode.JENKINS_STATUS_GIT_OK,
                        BaseConstantCode.JENKINS_STATUS_BUILD_OK,
                        BaseConstantCode.JENKINS_STATUS_INPUT_OK,
                        BaseConstantCode.JENKINS_STATUS_OUTPUT_OK,
                        BaseConstantCode.JENKINS_STATUS_DEPLOY_OK}, deployStatus);
                if (isExecuting) {
                    return true;
                }
            }
            return false;
        } else {
            // 不存在时直接返回false
            return false;
        }
    }

    /**
     * 沉睡时间
     *
     * @param seconds
     */
    private void sleepSeconds(long seconds) {
        try {
            Thread.sleep(seconds);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            logger.error("JenkinsServiceImpl sleep Exception : {}", ie);
        }
    }

}
